home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / GraphicsWorkshop / Source / Converters / jpeg.m < prev    next >
Encoding:
Text File  |  1993-01-14  |  16.9 KB  |  559 lines

  1. #import <stdio.h>
  2. #import <string.h>
  3. #import <jinclude.h>
  4. #import <streams/streams.h>
  5. #import <appkit/graphics.h>
  6. #import <appkit/NXBitmapImageRep.h>
  7. #import <setjmp.h>
  8. #import "NXBitmapImageRepControl.h"
  9. #import "ImageControl.h"
  10. #import "jpeg.h"
  11.  
  12. @implementation JPEG
  13.  
  14. /* These static variables are needed by the error routines. */
  15. static jmp_buf             setjmp_buffer;    // for return to caller
  16. static external_methods_ptr    emethods;        // needed for access to message_parm
  17.  
  18. static NXStream    *inStream;
  19. static NXStream    *outStream;
  20.  
  21. static JSAMPARRAY         color_map;        /* saves color map passed by quantizer */
  22. static NXBitmapImageRep    *outputImage;
  23. static NXBitmapImageRep    *inputImage;
  24. static ImageControl        *controls;
  25. static GetPixelNextFunc    nextPixelFunc;
  26. static ToGrayFunc            toGrayFunc;
  27. static unsigned char         *outData[5];
  28. static long                 offset = 0;
  29. static int                    curError;
  30. static int                 errState;
  31.  
  32. METHODDEF void trace_message (const char *msgtext)
  33. {
  34.     fprintf(stderr, msgtext,
  35.         emethods->message_parm[0], emethods->message_parm[1],
  36.         emethods->message_parm[2], emethods->message_parm[3],
  37.         emethods->message_parm[4], emethods->message_parm[5],
  38.         emethods->message_parm[6], emethods->message_parm[7]);
  39.     fprintf(stderr, "\n");   /* there is no \n in the format string! */
  40. }
  41.  
  42. METHODDEF void error_exit (const char *msgtext)
  43. {
  44.     trace_message(msgtext);   /* report the error message */
  45.     (*emethods->free_all) ();   /* clean up memory allocation & temp files */
  46.     longjmp(setjmp_buffer, 1);   /* return control to outer routine */
  47. }
  48.  
  49. METHODDEF void output_init (decompress_info_ptr cinfo)
  50. {
  51.     int                spp = 1;
  52.     NXColorSpace    cs = NX_OneIsWhiteColorSpace;
  53.     
  54.     if (cinfo->out_color_space == CS_GRAYSCALE) {
  55.         spp = 1;
  56.         cs = NX_OneIsWhiteColorSpace;
  57.     } else if (cinfo->out_color_space == CS_RGB) {
  58.         spp = 3;
  59.         cs = NX_RGBColorSpace;
  60.     } else if (cinfo->out_color_space == CS_CMYK) {
  61.         spp = 4;
  62.         cs = NX_CMYKColorSpace;
  63.     }
  64.  
  65.     outputImage = [NXBitmapImageRep alloc];
  66.     [outputImage     initData:            NULL
  67.                 pixelsWide:        cinfo->image_width
  68.                 pixelsHigh:        cinfo->image_height 
  69.                 bitsPerSample:    8
  70.                 samplesPerPixel:    spp
  71.                 hasAlpha:        NO
  72.                 isPlanar:            YES
  73.                 colorSpace:        cs
  74.                 bytesPerRow:        0
  75.                 bitsPerPixel:        0];
  76.     [outputImage getDataPlanes: outData];
  77. }
  78.  
  79. METHODDEF void put_pixel_rows (decompress_info_ptr cinfo, int num_rows, JSAMPIMAGE pixel_data)
  80. {
  81.     register long width = cinfo->image_width;
  82.     register int row;
  83.  
  84.     if (cinfo->out_color_space == CS_GRAYSCALE) {
  85.         for (row = 0; row < num_rows; row++) {
  86.             bcopy(pixel_data[0][row], outData[0] + offset, width);
  87.             offset += width;
  88.         }
  89.     } else if (cinfo->out_color_space == CS_RGB) {
  90.         for (row = 0; row < num_rows; row++) {
  91.             bcopy(pixel_data[0][row], outData[0] + offset, width);
  92.             bcopy(pixel_data[1][row], outData[1] + offset, width);
  93.             bcopy(pixel_data[2][row], outData[2] + offset, width);
  94.             offset += width;
  95.         }
  96.     } else if (cinfo->out_color_space == CS_CMYK) {
  97.         for (row = 0; row < num_rows; row++) {
  98.             bcopy(pixel_data[0][row], outData[0] + offset, width);
  99.             bcopy(pixel_data[1][row], outData[1] + offset, width);
  100.             bcopy(pixel_data[2][row], outData[2] + offset, width);
  101.             bcopy(pixel_data[3][row], outData[3] + offset, width);
  102.             offset += width;
  103.         }
  104.     }
  105. }
  106.  
  107. METHODDEF void put_color_map (decompress_info_ptr cinfo, int num_colors, JSAMPARRAY colormap)
  108. {
  109.     color_map = colormap;                    /* save for use in output */
  110. #ifdef DEBUG
  111.     fprintf(stderr, "This shouldn't get  called\n");
  112. #endif
  113. //    cinfo->methods->put_pixel_rows = put_demapped_rows;
  114. }
  115.  
  116. METHODDEF void output_term (decompress_info_ptr cinfo)
  117. {
  118. #ifdef DEBUG
  119.     fprintf(stderr, "Image was sucessfully read\n");
  120. #endif
  121.     errState = CONVERT_ERR_NONE;
  122.     curError = ERROR_NO_ERROR;
  123. }
  124.  
  125. /*
  126.  * This routine gets control after the input file header has been read.
  127.  * It must determine what output file format is to be written,
  128.  * and make any other decompression parameter changes that are desirable.
  129.  */
  130.  
  131. METHODDEF void d_ui_method_selection (decompress_info_ptr cinfo)
  132. {
  133. #ifdef DEBUG
  134.     fprintf(stderr, "Output format select\n");
  135. #endif
  136.     cinfo->methods->output_init = output_init;
  137.     cinfo->methods->put_color_map = put_color_map;
  138.     cinfo->methods->put_pixel_rows = put_pixel_rows;
  139.     cinfo->methods->output_term = output_term;
  140. }
  141.  
  142.  
  143. /*
  144.  * Reload the input buffer after it's been emptied, and return the next byte.
  145.  * See the JGETC macro for calling conditions.
  146.  *
  147.  * This routine would need to be replaced if reading JPEG data from something
  148.  * other than a stdio stream.
  149.  */
  150.  
  151. METHODDEF void progress_monitor (decompress_info_ptr cinfo, long loopcounter, long looplimit)
  152. {
  153.     fprintf(stderr, "%d%%...", (int)((float)loopcounter / (float)looplimit * 100.0));
  154. }
  155.  
  156. METHODDEF int read_jpeg_data (decompress_info_ptr cinfo)
  157. {
  158.     cinfo->next_input_byte = cinfo->input_buffer + MIN_UNGET;
  159.     cinfo->bytes_in_buffer = NXRead(    inStream, 
  160.                                     cinfo->next_input_byte, 
  161.                                     JPEG_BUF_SIZE);
  162.  
  163.     if (cinfo->bytes_in_buffer <= 0) {
  164.         curError = ERROR_TRUNCATED_FILE;
  165.         errState = CONVERT_ERR_WARNING;
  166.         ERREXIT(cinfo->emethods, "Unexpected EOF in JPEG file");
  167.     }
  168.  
  169.     return JGETC(cinfo);
  170. }
  171.  
  172. - init
  173. {
  174.     return self;
  175. }
  176.  
  177. - free
  178. {
  179.     return [super free];
  180. }
  181.  
  182. - readFromStream: (NXStream *)stream from: sender;
  183. {
  184.     struct decompress_info_struct        cinfo;
  185.     struct decompress_methods_struct    dc_methods;
  186.     struct external_methods_struct        e_methods;
  187.  
  188.     curError = ERROR_NO_ERROR;
  189.     errState = CONVERT_ERR_NONE;
  190.  
  191.     /* Initialize the system-dependent method pointers. */
  192.     cinfo.methods = &dc_methods;
  193.     cinfo.emethods = &e_methods;
  194.  
  195.     emethods = &e_methods;                // save struct addr for possible access
  196.     e_methods.error_exit = error_exit;        // supply error-exit routine
  197.     e_methods.trace_message = trace_message; // supply trace-message routine
  198.  
  199.     /* prepare setjmp context for possible exit from error_exit */
  200.     if (setjmp(setjmp_buffer)) {
  201.         /* If we get here, the JPEG code has signaled an error.
  202.          * Memory allocation has already been cleaned up (see free_all call in
  203.          * error_exit), but we need to close the input file before returning.
  204.          * You might also need to close an output file, etc.
  205.          */
  206.          fprintf(stderr, "Bummer, failing\n");
  207.         NXClose(outStream);
  208.         NXClose(inStream);
  209.         if (errState != CONVERT_ERR_NONE) {
  210.             curError = ERROR_UNABLE_TO_OPEN;
  211.             errState = CONVERT_ERR_FATAL;
  212.         }
  213.         return outputImage;
  214.     }
  215.     
  216.     jselmemmgr(&e_methods);                /* memory allocation routines */
  217.     dc_methods.d_ui_method_selection = d_ui_method_selection;
  218.  
  219.     j_d_defaults(&cinfo, TRUE);
  220.  
  221.     dc_methods.read_jpeg_data = read_jpeg_data;
  222.     dc_methods. progress_monitor = progress_monitor;
  223.  
  224.     /* Set up default input and output file references. */
  225.     /* (These may be overridden below.) */
  226.     inStream = stream;
  227.     cinfo.output_file = stderr;
  228.     outStream = NXOpenFile(fileno(stdout), NX_WRITEONLY);
  229.  
  230.     /* Set up default parameters. */
  231.     e_methods.trace_level             = 0;
  232.     cinfo.output_gamma             = 1.0;
  233.     cinfo.quantize_colors             = FALSE;
  234.     cinfo.two_pass_quantize         = FALSE;
  235.     cinfo.use_dithering             = FALSE;
  236.     cinfo.desired_number_of_colors    = 256;
  237.     cinfo.do_block_smoothing         = FALSE;
  238.     cinfo.do_pixel_smoothing         = FALSE;
  239. //    cinfo.out_color_space             = CS_RGB;
  240.     cinfo.jpeg_color_space             = CS_UNKNOWN;
  241.     /* setting any other value in jpeg_color_space overrides heuristics */
  242.     /* in jrdjfif.c ... */
  243.     /* You may wanta change the default output format; here's the place: */
  244.     cinfo.do_block_smoothing         = FALSE;
  245.  
  246.     offset = 0;
  247.  
  248.     jselrjfif(&cinfo);
  249.     
  250.     /* Do it to it! */
  251.     jpeg_decompress(&cinfo);
  252.  
  253.     /* Release memory. */
  254.  
  255.     NXClose(outStream);
  256.     NXClose(inStream);
  257.  
  258.     return outputImage;
  259. }
  260.  
  261. METHODDEF void progress_monitorII(compress_info_ptr cinfo, long loopcounter, long looplimit)
  262. {
  263.     fprintf(stderr, "%d%%...", (int)((float)loopcounter / (float)looplimit * 100.0));
  264. }
  265.  
  266. METHODDEF void input_init (compress_info_ptr cinfo)
  267. /* Initialize for input; return image size and component data. */
  268. {
  269.     /* This routine must return five pieces of information about the incoming
  270.      * image, and must do any setup needed for the get_input_row routine.
  271.      * The image information is returned in fields of the cinfo struct.
  272.      * (If you don't care about modularity, you could initialize these fields
  273.      * in the main JPEG calling routine, and make this routine be a no-op.)
  274.      * We show some example values here.
  275.      */
  276.     cinfo->image_width = [inputImage pixelsWide];   /* width in pixels */
  277.     cinfo->image_height = [inputImage pixelsHigh];   /* width in pixels */
  278.     /* JPEG views an image as being a rectangular array of pixels, with each
  279.      * pixel having the same number of "component" values (color channels).
  280.      * You must specify how many components there are and the colorspace
  281.      * interpretation of the components.  Most applications will use RGB data or
  282.      * grayscale data.  If you want to use something else, you'll need to study
  283.      * and perhaps modify jcdeflts.c, jccolor.c, and jdcolor.c.
  284.      */
  285.     switch ([inputImage colorSpace]) {
  286.            case NX_OneIsWhiteColorSpace:
  287.             cinfo->input_components = 1;
  288.             cinfo->in_color_space = CS_GRAYSCALE;
  289.             cinfo->data_precision = 8;
  290.             break;
  291.         case NX_OneIsBlackColorSpace:
  292.             cinfo->input_components = 1;
  293.             cinfo->in_color_space = CS_GRAYSCALE;
  294.             cinfo->data_precision = 8;
  295.             break;
  296.         case NX_RGBColorSpace:
  297.             cinfo->input_components = 3;
  298.             cinfo->in_color_space = CS_RGB;
  299.             cinfo->data_precision = 8;
  300.             break;
  301.         case NX_CMYKColorSpace:
  302.             cinfo->input_components = 4;
  303.             cinfo->in_color_space = CS_CMYK;
  304.             cinfo->data_precision = 8;
  305.             break;
  306.         default:
  307.             fprintf(stderr, "Can't deal with this!\n");
  308.     }
  309.     fprintf(stderr, "%d %d %d\n", cinfo->input_components, cinfo->in_color_space, cinfo->data_precision);
  310. }
  311.  
  312. METHODDEF void
  313. get_input_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row)
  314. /* Read next row of pixels into pixel_row[][] */
  315. {
  316.     /* This example shows how you might read RGB data (3 components)
  317.      * from an input file in which the data is stored 3 bytes per pixel
  318.      * in left-to-right, top-to-bottom order.
  319.      */
  320.     register FILE            * infile = cinfo->input_file;
  321.     register JSAMPROW    ptr0, ptr1, ptr2, ptr3;
  322.     register long            col;
  323.     register Pixel            *myPixel;
  324.  
  325.     switch ([inputImage colorSpace]) {
  326.            case NX_OneIsWhiteColorSpace:
  327.             ptr0 = pixel_row[0];
  328.             for (col = 0; col < cinfo->image_width; col++) {
  329.                 *ptr0++ = (JSAMPLE)(255 - nextPixelFunc()->values[0]);
  330.             }
  331.             break;
  332.         case NX_OneIsBlackColorSpace:
  333.             ptr0 = pixel_row[0];
  334.             for (col = 0; col < cinfo->image_width; col++) {
  335.                 *ptr0++ = (JSAMPLE)(nextPixelFunc()->values[0]);
  336.             }
  337.             break;
  338.         case NX_RGBColorSpace:
  339.             ptr0 = pixel_row[0];
  340.             ptr1 = pixel_row[1];
  341.             ptr2 = pixel_row[2];
  342.             for (col = 0; col < cinfo->image_width; col++) {
  343.                 myPixel = nextPixelFunc();
  344.                 *ptr0++ = (JSAMPLE)(myPixel->values[0]);
  345.                 *ptr1++ = (JSAMPLE)(myPixel->values[1]);
  346.                 *ptr2++ = (JSAMPLE)(myPixel->values[2]);
  347.             }
  348.             break;
  349.         case NX_CMYKColorSpace:
  350.             ptr0 = pixel_row[0];
  351.             ptr1 = pixel_row[1];
  352.             ptr2 = pixel_row[2];
  353.             ptr3 = pixel_row[3];
  354.             for (col = 0; col < cinfo->image_width; col++) {
  355.                 myPixel = nextPixelFunc();
  356.                 *ptr0++ = (JSAMPLE)(myPixel->values[0]);
  357.                 *ptr1++ = (JSAMPLE)(myPixel->values[1]);
  358.                 *ptr2++ = (JSAMPLE)(myPixel->values[2]);
  359.                 *ptr3++ = (JSAMPLE)(myPixel->values[3]);
  360.             }
  361.             break;
  362.         default:
  363.             fprintf(stderr, "Can't deal with this!\n");
  364.     }
  365. }
  366.  
  367. METHODDEF void input_term (compress_info_ptr cinfo)
  368. /* Finish up at the end of the input */
  369. {
  370.     /* This termination routine will very often have no work to do, */
  371.     /* but you must provide it anyway. */
  372.     /* Note that the JPEG code will only call it during successful exit; */
  373.     /* if you want it called during error exit, you gotta do that yourself. */
  374. }
  375.  
  376. METHODDEF void c_ui_method_selection (compress_info_ptr cinfo)
  377. {
  378.     jselwjfif(cinfo);
  379. }
  380.  
  381. - (BOOL)write: (id)image toStream: (NXStream *)stream from: sender;
  382. {
  383.     /* These three structs contain JPEG parameters and working data.
  384.      * They must survive for the duration of parameter setup and one
  385.      * call to jpeg_compress; typically, making them local data in the
  386.      * calling routine is the best strategy.
  387.      */
  388.     struct compress_info_struct            cinfo;
  389.     struct compress_methods_struct        c_methods;
  390.     struct external_methods_struct        e_methods;
  391.  
  392.     inputImage = image;
  393.     controls = [sender getImageControl: inputImage];
  394.     nextPixelFunc = [controls getNextFunction];
  395.     toGrayFunc = [controls getToGrayFunction];
  396.     [controls resetNext];
  397.     
  398.     /* Initialize the system-dependent method pointers. */
  399.     cinfo.methods = &c_methods;            /* links to method structs */
  400.     cinfo.emethods = &e_methods;
  401.  
  402.     e_methods.error_exit = error_exit;        // supply error-exit routine
  403.  
  404.     /* prepare setjmp context for possible exit from error_exit */
  405.     if (setjmp(setjmp_buffer)) {
  406.         /* If we get here, the JPEG code has signaled an error.
  407.          * Memory allocation has already been cleaned up (see free_all call in
  408.          * error_exit), but we need to close the input file before returning.
  409.          * You might also need to close an output file, etc.
  410.          */
  411.          fprintf(stderr, "Bummer, failing\n");
  412.         if (errState != CONVERT_ERR_NONE) {
  413.             curError = ERROR_UNABLE_TO_WRITE;
  414.             errState = CONVERT_ERR_FATAL;
  415.         }
  416.         return NO;
  417.     }
  418.     
  419.     /* Here we use the default JPEG error handler, which will just print
  420.      * an error message on stderr and call exit().  See the second half of
  421.      * this file for an example of more graceful error recovery.
  422.      */
  423.     jselerror(&e_methods);   /* select std error/trace message routines */
  424.     /* Here we use the standard memory manager provided with the JPEG code.
  425.      * In some cases you might want to replace the memory manager, or at
  426.      * least the system-dependent part of it, with your own code.
  427.      */
  428.     jselmemmgr(&e_methods);   /* select std memory allocation routines */
  429.     /* If the compressor requires full-image buffers (for entropy-coding
  430.      * optimization or a noninterleaved JPEG file), it will create temporary
  431.      * files for anything that doesn't fit within the maximum-memory setting.
  432.      * (Note that temp files are NOT needed if you use the default parameters.)
  433.      * You can change the default maximum-memory setting by changing
  434.      * e_methods.max_memory_to_use after jselmemmgr returns.
  435.      * On some systems you may also need to set up a signal handler to
  436.      * ensure that temporary files are deleted if the program is interrupted.
  437.      * (This is most important if you are on MS-DOS and use the jmemdos.c
  438.      * memory manager back end; it will try to grab extended memory for
  439.      * temp files, and that space will NOT be freed automatically.)
  440.      * See jcmain.c or jdmain.c for an example signal handler.
  441.      */
  442.  
  443.     /* Here, set up pointers to your own routines for input data handling
  444.      * and post-init parameter selection.
  445.      */
  446.     c_methods.input_init = input_init;
  447.     c_methods.get_input_row = get_input_row;
  448.     c_methods.input_term = input_term;
  449.     c_methods.c_ui_method_selection = c_ui_method_selection;
  450.  
  451.     /* Set up default JPEG parameters in the cinfo data structure. */
  452.     j_c_defaults(&cinfo, 75, FALSE);
  453.     /* Note: 75 is the recommended default quality level; you may instead pass
  454.      * a user-specified quality level.  Be aware that values below 25 will cause
  455.      * non-baseline JPEG files to be created (and a warning message to that
  456.      * effect to be emitted on stderr).  This won't bother our decoder, but some
  457.      * commercial JPEG implementations may choke on non-baseline JPEG files.
  458.      * If you want to force baseline compatibility, pass TRUE instead of FALSE.
  459.      * (If non-baseline files are fine, but you could do without that warning
  460.      * message, set e_methods.trace_level to -1.)
  461.      */
  462.  
  463.     /* Select the input and output files.
  464.      * Note that cinfo.input_file is only used if your input reading routines
  465.      * use it; otherwise, you can just make it NULL.
  466.      * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
  467.      * requires it in order to write binary files.
  468.      */
  469.  
  470.     c_methods. progress_monitor = progress_monitorII;
  471.  
  472.     cinfo.input_file = NULL;   /* if no actual input file involved */
  473.  
  474.     cinfo.output_file = (FILE *)stream;
  475.  
  476.     /* Here we go! */
  477.     jpeg_compress(&cinfo);
  478.  
  479.     /* That's it, son.  Nothin' else to do, except close files. */
  480.     /* Here we assume only the output file need be closed. */
  481.     NXFlush(((NXStream *)(cinfo.output_file)));
  482.  
  483.     /* Note: if you want to compress more than one image, we recommend you
  484.      * repeat this whole routine.  You MUST repeat the j_c_defaults()/alter
  485.      * parameters/jpeg_compress() sequence, as some data structures allocated
  486.      * in j_c_defaults are freed upon exit from jpeg_compress.
  487.      */
  488.     [controls free];
  489.  
  490.     return YES;
  491. }
  492.  
  493. - readAllFromStream: (NXStream *)stream from: sender
  494. {
  495.     return nil;
  496. }
  497.  
  498. - (BOOL)writeAll: (id)image toStream: (NXStream *)stream
  499. {
  500.     return NO;
  501. }
  502.  
  503. - customSaveView: (int)width
  504. {
  505.     return nil;
  506. }
  507.  
  508. - customOpenView: (int)width
  509. {
  510.     return nil;
  511. }
  512.  
  513. - (char *)getFormatName
  514. {
  515.     return("Stand Alone JPEG Compressed File (JFIF)");
  516. }
  517.  
  518.  - (BOOL)setCustomParameter: (const char *)parameter withValue: (void *)ptr
  519. {
  520.     return NO;
  521. }
  522.  
  523.  - (void *)getCustomParameter: (const char *)parameter
  524. {
  525.     return NULL;
  526. }
  527.  
  528. - (char *)copyrightNotice
  529. {
  530.     return "JPEG Converter\nby Alex Raftis\n\nCopyright (c) 1991 Cal Poly State University\nCopyright (c) 1991 The Independent JPEG Group\n\nEmail bugs to alex@data.ACS.CalPoly.EDU";
  531. }
  532.  
  533. - (int)errorState
  534. {
  535.     return errState;
  536. }
  537.  
  538. - (int)errorMessage
  539. {
  540.     return curError;
  541. }
  542.  
  543. - (char *)errorStringMessage
  544. {
  545.     return NULL;
  546. }
  547.  
  548. - (BOOL)needsWindowServer;
  549. {
  550.     return NO;
  551. }
  552.  
  553. - (char *)protocolVersion
  554. {
  555.     return "1.0";
  556. }
  557.  
  558. @end
  559.